﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/8 14:37:08
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using ImageK.Gui;
using ImageK.IO;
using ImageK.Java;
using ImageK.Java.Extensions;
using ImageK.Plugin.Filter;
using ImageK.Process;
using ImageK.Text;
using ImageK.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace ImageK
{
    /// <summary>
    /// This class contains the ImageJ preferences, which are 
    /// loaded from the "IJ_Props.txt" and "IJ_Prefs.txt" files.
    /// </summary>
    public class Prefs
    {
		public static string PROPS_NAME = "IJ_Props.txt";
	    public static string PREFS_NAME = "IJ_Prefs.txt";
	    public static string DIR_IMAGE = "dir.image";
	    public static string FCOLOR = "fcolor";
	    public static string BCOLOR = "bcolor";
	    public static string ROICOLOR = "roicolor";
	    public static string SHOW_ALL_COLOR = "showcolor";
	    public static string JPEG = "jpeg";
	    public static string FPS = "fps";
        public static string DIV_BY_ZERO_VALUE = "div-by-zero";
        public static string NOISE_SD = "noise.sd";
        public static string MENU_SIZE = "menu.size";
        public static string GUI_SCALE = "gui.scale";
        public static string THREADS = "threads";
	    public static string KEY_PREFIX = ".";
 
	    private static int USE_POINTER = 1 << 0, ANTIALIASING = 1 << 1, INTERPOLATE = 1 << 2, ONE_HUNDRED_PERCENT = 1 << 3,
		BLACK_BACKGROUND = 1 << 4, JFILE_CHOOSER = 1 << 5, UNUSED = 1 << 6, BLACK_CANVAS = 1 << 7, WEIGHTED = 1 << 8,
		AUTO_MEASURE = 1 << 9, REQUIRE_CONTROL = 1 << 10, USE_INVERTING_LUT = 1 << 11, ANTIALIASED_TOOLS = 1 << 12,
		INTEL_BYTE_ORDER = 1 << 13, DOUBLE_BUFFER = 1 << 14, NO_POINT_LABELS = 1 << 15, NO_BORDER = 1 << 16,
		SHOW_ALL_SLICE_ONLY = 1 << 17, COPY_HEADERS = 1 << 18, NO_ROW_NUMBERS = 1 << 19,
		MOVE_TO_MISC = 1 << 20, ADD_TO_MANAGER = 1 << 21, RUN_SOCKET_LISTENER = 1 << 22,
		MULTI_POINT_MODE = 1 << 23, ROTATE_YZ = 1 << 24, FLIP_XZ = 1 << 25,
		DONT_SAVE_HEADERS = 1 << 26, DONT_SAVE_ROW_NUMBERS = 1 << 27, NO_CLICK_TO_GC = 1 << 28,
		AVOID_RESLICE_INTERPOLATION = 1 << 29, KEEP_UNDO_BUFFERS = 1 << 30;
		public static string OPTIONS = "prefs.options";
    
	    public static string vistaHint = "";  // no longer used

	    private static int USE_SYSTEM_PROXIES = 1 << 0, USE_FILE_CHOOSER = 1 << 1,
		SUBPIXEL_RESOLUTION = 1 << 2, ENHANCED_LINE_TOOL = 1 << 3, SKIP_RAW_DIALOG = 1 << 4,
		REVERSE_NEXT_PREVIOUS_ORDER = 1 << 5, AUTO_RUN_EXAMPLES = 1 << 6, SHOW_ALL_POINTS = 1 << 7,
		DO_NOT_SAVE_WINDOW_LOCS = 1 << 8, JFILE_CHOOSER_CHANGED = 1 << 9,
		CANCEL_BUTTON_ON_RIGHT = 1 << 10, IGNORE_RESCALE_SLOPE = 1 << 11,
		NON_BLOCKING_DIALOGS = 1 << 12, FIXED_DICOM_SCALINGg = 1 << 13;
		public static string OPTIONS2 = "prefs.options2";

        /** file.separator system property */
        // public static string separator = System.getProperty("file.separator");
        public static string separator = Path.DirectorySeparatorChar.ToString();
		/** Use pointer cursor instead of cross */
		public static bool usePointerCursor;
		/** No longer used */
		public static bool antialiasedText;
		/** Display images scaled <100% using bilinear interpolation */
		public static bool interpolateScaledImages;
		/** Open images at 100% magnification*/
		public static bool open100Percent;
		/** Backgound is black in binary images*/
		public static bool blackBackground;
		/** Use JFileChooser instead of FileDialog to open and save files. */
		public static bool useJFileChooser;
		/** Color to grayscale conversion is weighted (0.299, 0.587, 0.114) if the variable is true. */
		public static bool weightedColor;
		/** Use black image border. */
		public static bool blackCanvas;
		/** Point tool auto-measure mode. */
		public static bool pointAutoMeasure;
		/** Point tool auto-next slice mode (not saved in IJ_Prefs). */
		public static bool pointAutoNextSlice;
		/** Require control or command key for keybaord shortcuts. */
		public static bool requireControlKey;
		/** Open 8-bit images with inverting LUT so 0 is white and 255 is black. */
		public static bool useInvertingLut;
		/** Draw tool icons using antialiasing (always true). */
		public static bool antialiasedTools = true;
		/** Export TIFF and Raw using little-endian byte order. */
		public static bool intelByteOrder = true;
		/** No longer used */
		public static bool doubleBuffer = true;
		/** Do not label multiple points created using point tool. */
		public static bool noPointLabels;
		/** Disable Edit/Undo command. */
		public static bool disableUndo;
		/** Do not draw black border around image. */
		public static bool noBorder;
		/** Only show ROIs associated with current slice in Roi Manager "Show All" mode. */
		public static bool showAllSliceOnly = true;
		/** Include column headers when copying tables to clipboard. */
		public static bool copyColumnHeaders;
		/** Do not include row numbers when copying tables to clipboard. */
		public static bool noRowNumbers;
		/** Move isolated plugins to Miscellaneous submenu. */
		public static bool moveToMisc;
		/** Add points to ROI Manager. */
		public static bool pointAddToManager;
		/** Add points to overlay. */
		public static bool pointAddToOverlay;
		/** Extend the borders to foreground for binary erosions and closings. */
		public static bool padEdges;
		/** Run the SocketListener. */
		public static bool runSocketListener;
		/** Use MultiPoint tool. */
		public static bool multiPointMode;
		/** Open DICOMs as 32-bit float images */
		public static bool openDicomsAsFloat;
		/** Ignore Rescale Slope when opening DICOMs */
		public static bool ignoreRescaleSlope;
		/** Assume DICOM volumes use identical RescaleSlope and RescaleIntercept across all slices */
		public static bool fixedDicomScaling;
		/** Plot rectangular selectons vertically */
		public static bool verticalProfile;
		/** Rotate YZ orthogonal views 90 degrees */
		public static bool rotateYZ;
		/** Rotate XZ orthogonal views 180 degrees */
		public static bool flipXZ;
		/** Don't save Results table column headers */
		public static bool dontSaveHeaders;
		/** Don't save Results table row numbers */
		public static bool dontSaveRowNumbers;
		/** Don't run garbage collector when user clicks in status bar */
		public static bool noClickToGC;
		/** Angle tool measures reflex angle */
		public static bool reflexAngle;
		/** Avoid interpolation when re-slicing */
		public static bool avoidResliceInterpolation;
		/** Preserve undo (snapshot) buffers when switching images */
		public static bool keepUndoBuffers;
		/** Use ROI names as "show all" labels in the ROI Manager */
		public static bool useNamesAsLabels;
		/** Set the "java.net.useSystemProxies" property */
		public static bool useSystemProxies;
		/** Use the file chooser to import and export image sequences on Windows and Linux*/
		public static bool useFileChooser;
		/** Use sub-pixel resolution with line selections */
		public static bool subPixelResolution;
		/** Adjust contrast when scrolling stacks */
		public static bool autoContrast;
		/** Allow lines to be created with one click at start and another at the end */
		public static bool enhancedLineTool;
		/** Keep arrow selection after adding to overlay */
		public static bool keepArrowSelections;
		/** Aways paint images using double buffering */
		public static bool paintDoubleBuffered;
		/** Do not display dialog when opening .raw files */
		public static bool skipRawDialog;
		/** Reverse channel-slice-frame priority used by Next Slice and Previous Slice commands. */
		public static bool reverseNextPreviousOrder;
		/** Automatically run examples in Help/Examples menu. */
		public static bool autoRunExamples = true;
		/** Ignore stack positions when displaying points. */
		public static bool showAllPoints;

		//todo
        /** Set MenuBar on Macs running Java 8. */
        // public static bool setIJMenuBar = IJ.isMacOSX();
        public static bool setIJMenuBar = false;

		/** "ImageJ" window is always on top. */
		public static bool alwaysOnTop;
		/** Automatically spline fit line selections */
		public static bool splineFitLines;
		/** Enable this option to workaround a bug with some Linux window
			managers that causes windows to wander down the screen. */
		public static bool doNotSaveWindowLocations;
		/** Use JFileChooser setting changed/ */
		public static bool jFileChooserSettingChanged;
		/** Convert tiff units to microns if pixel width is less than 0.0001 cm. */
		public static bool convertToMicrons = true;
		/** Wand tool "Smooth if thresholded" option */
		public static bool smoothWand;
		/** "Close All" command running */
		public static bool closingAll;
		/** Dialog "Cancel" button is on right on Linux */
		public static bool dialogCancelButtonOnRight;
		/** Support TRANSFORM Undo in macros */
		public static bool supportMacroUndo;
		/** Use NonBlockingGenericDialogs in filters */
		public static bool nonBlockingFilterDialogs;
		/** Turn live display on plots automatically */
		public static bool autoLivePlots;

		//Save location of moved image windows */	
		//public static bool saveImageLocation = true;

		static bool commandLineMacro;

        private static Dictionary<string, string> ijPrefs = new Dictionary<string, string>();
		// static Properties ijPrefs = new Properties();
		// static Properties props = new Properties(ijPrefs);
		private static Dictionary<string, string> props = new Dictionary<string, string>();

        static string prefsDir;
		static string imagesURL;
		static string ImageJDir;
		static string pluginsDirProperty;
		static int threads;
		static int transparentIndex = -1;
		private static bool _resetPreferences;
		private static double guiScale = 1.0;
		// private static Properties locKeys = new Properties();
		private static string propertiesPath; // location of custom IJ_Props.txt
		private static string preferencesPath; // location of custom IJ_Prefs.txt



        /** Finds and loads the configuration file ("IJ_Props.txt")
         * and the preferences file ("IJ_Prefs.txt").
         * @return	an error message if "IJ_Props.txt" not found.
        */
        public static string load(object ij)
        {
            if (ImageJDir==null)
                ImageJDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

            //todo:
            // Plugins installed in the File/New submenu
            props.Add("new01", "\"Image...[n]\",ImageK.Plugin.Commands(\"new\")");
            props.Add("new02", "\"Hyperstack...\",ImageK.Plugin.HyperStackMaker");
            props.Add("new03", "\"Text Window[N]\",ImageK.Plugin.NewPlugin(\"text\")");
            props.Add("new04", "\"Internal Clipboard\",ImageK.Plugin.Clipboard(\"show\")");
            props.Add("new05", "\"System Clipboard[V]\",ImageK.Plugin.Clipboard(\"showsys\")");


            //Plugins installed in the File/Save As submenu
            props.Add("save01", "\"Tiff...\",ImageK.Plugin.Filter.Writer(\"tiff\")");
            props.Add("save02", "\"Gif...\",ImageK.Plugin.Filter.Writer(\"gif\")");
            props.Add("save03", "\"Jpeg...\",ImageK.Plugin.Filter.Writer(\"jpeg\")");
            props.Add("save04", "\"Text Image...\",ImageK.Plugin.Filter.Writer(\"text\")");
            props.Add("save05", "\"ZIP...\",ImageK.Plugin.Filter.Writer(\"zip\")");
            props.Add("save06", "\"Raw Data...\",ImageK.Plugin.Filter.Writer(\"raw\")");
            props.Add("save07", "\"Image Sequence... \",ImageK.Plugin.StackWriter");
            props.Add("save08", "\"AVI... \",ImageK.Plugin.Filter.AVI_Writer");
            props.Add("save09", "\"BMP...\",ImageK.Plugin.Filter.Writer(\"bmp\")");
            props.Add("save10", "\"PNG...\",ImageK.Plugin.Filter.Writer(\"png\")");
            props.Add("save11", "\"PGM...\",ImageK.Plugin.Filter.Writer(\"pgm\")");
            props.Add("save12", "\"FITS...\",ImageK.Plugin.Filter.Writer(\"fits\")");
            props.Add("save13", "\"LUT...\",ImageK.Plugin.Filter.Writer(\"lut\")");
            props.Add("save14", "\"Selection...\",ImageK.Plugin.Filter.RoiWriter");
            props.Add("save15", "\"XY Coordinates...\",ImageK.Plugin.Filter.XYWriter");
            props.Add("save16", "\"Results...\",ImageK.Plugin.MeasurementsWriter");
            props.Add("save17", "\"Text...\",ImageK.Plugin.TextWriter");

            // Plugins installed in the Edit/Selection submenu
            props.Add("selection01", "\"Select All[a]\",ImageK.Plugin.Selection(\"all\")");
            props.Add("selection02", "\"Select None[A]\",ImageK.Plugin.Selection(\"none\")");
            props.Add("selection03", "\"Restore Selection[E]\",ImageK.Plugin.Selection(\"restore\")");
            props.Add("selection04", "\"Fit Spline\",ImageK.Plugin.Selection(\"spline\")");
            props.Add("selection05", "\"Fit Circle\", ImageK.Plugin.Selection(\"circle\")");
            props.Add("selection06", "\"Fit Ellipse\",ImageK.Plugin.Selection(\"ellipse\")");
            props.Add("selection07", "\"Fit Rectangle\",ImageK.Plugin.Selection(\"rect\")");
            props.Add("selection08", "\"Interpolate\",ImageK.Plugin.Selection(\"interpolate\")");
            props.Add("selection09", "\"Convex Hull\",ImageK.Plugin.Selection(\"hull\")");
            props.Add("selection10", "\"Make Inverse\",ImageK.Plugin.Selection(\"inverse\")");
            props.Add("selection11", "\"Create Selection\",ImageK.Plugin.Selection(\"from\")");
            props.Add("selection12", "\"Create Mask\", ImageK.Plugin.Selection(\"mask\")");
            // selection13=-
            //     selection14="Properties... [y]",ij.plugin.Selection("properties")
            // selection15="Scale... ",ij.plugin.RoiScaler
            //     selection16 = "Rotate...",ij.plugin.Selection("rotate")
            // selection17="Translate... ",ij.plugin.Selection("translate")
            // selection18="Enlarge...",ij.plugin.RoiEnlarger
            //     selection19 = "Make Band...",ij.plugin.Selection("band")
            // selection20="Specify...",ij.plugin.SpecifyROI
            //     selection21 = "Straighten...",ij.plugin.Straightener
            //     selection22 = "To Bounding Box",ij.plugin.Selection("tobox")
            // selection23="Line to Area",ij.plugin.Selection("toarea")
            // selection24="Area to Line",ij.plugin.Selection("toline")
            // selection25="Image to Selection...",ij.plugin.OverlayCommands("image-roi")
            // selection26="Add to Manager[t]",ij.plugin.Selection("add")

            // Plugins installed in the Edit/Options submenu
            props.Add("options01", "\"Appearance...\",ImageK.Plugin.AppearanceOptions");
            props.Add("options02", "\"Arrow Tool...\",ImageK.Plugin.ArrowToolOptions");
            props.Add("options03", "\"Colors...\",ImageK.Plugin.Colors");
            props.Add("options04", "\"Compiler...\",ImageK.Plugin.Compiler(\"options\")");
            props.Add("options05", "\"Conversions...\",ImageK.Plugin.Options(\"conv\")");
            props.Add("options06", "\"DICOM...\",ImageK.Plugin.Options(\"dicom\")");
            props.Add("options07", "\"Fonts...\",ImageK.Plugin.SimpleCommands(\"fonts\")");
            props.Add("options08", "\"Input / Output...\",ImageK.Plugin.Options(\"io\")");
            props.Add("options09", "\"Line Width...\",ImageK.Plugin.Options(\"line\")");
            props.Add("options10", "\"Memory & Threads...\",ImageK.Plugin.Memory");
            props.Add("options11", "\"Misc...\",ImageK.Plugin.Options(\"misc\")");
            props.Add("options12", "\"Plots...\",ImageK.Plugin.Profiler(\"set\")");
            props.Add("options13", "\"Point Tool...\",ImageK.Plugin.PointToolOptions");
            props.Add("options14", "\"Proxy Settings...\",ImageK.Plugin.ProxySettings");
            props.Add("options15", "\"Roi Defaults...\",ImageK.Gui.RoiDefaultsDialog");
            props.Add("options16", "\"Rounded Rect Tool...\",ImageK.Plugin.RectToolOptions");
            props.Add("options17", "\"Startup...\",ImageK.Plugin.Startup");
            props.Add("options18", "\"Wand Tool...\",ImageK.Plugin.WandToolOptions");
            props.Add("options19", "-");
            props.Add("options20", "\"Fresh Start\",ImageK.Plugin.Options(\"fresh - start\")");
            props.Add("options21", "\"Reset... \",ImageK.Plugin.Options(\"reset\")");


			// Plugins installed in the Image/ Adjust submenu
			props.Add("adjust01", "\"Brightness / Contrast...[C]\",ImageK.Plugin.Frame.ContrastAdjuster");
			props.Add("adjust02", "\"Window / Level...\",ImageK.Plugin.Frame.ContrastAdjuster(\"wl\")");
			props.Add("adjust03", "\"Color Balance...\",ImageK.Plugin.Frame.ContrastAdjuster(\"balance\")");
			props.Add("adjust04", "\"Threshold...[T]\",ImageK.Plugin.Frame.ThresholdAdjuster");
			props.Add("adjust05", "\"Color Threshold...\",ImageK.Plugin.Frame.ColorThresholder");
			props.Add("adjust06", "\"Size...\",ImageK.Plugin.Resizer");
			props.Add("adjust07", "\"Canvas Size...\",ImageK.Plugin.CanvasResizer");
			props.Add("adjust08", "\"Line Width... \",ImageK.Plugin.frame.LineWidthAdjuster");
			props.Add("adjust09", "\"Coordinates...\",ImageK.Plugin.Coordinates");

            //# Plugins installed in the Image/Color submenu
            props.Add("color01", "\"Split Channels\", ImageK.Plugin.ChannelSplitter");
			props.Add("color02", "\"Merge Channels...\",ImageK.Plugin.RGBStackMerge");
			props.Add("color03", "\"Arrange Channels...\",ImageK.Plugin.ChannelArranger");
			props.Add("color04", "\"Invert LUTs\",ImageK.Plugin.SimpleCommands(\"invert\")");
			props.Add("color05", "\"Channels Tool...[Z]\",ImageK.Plugin.frame.Channels");
			props.Add("color06", "-");
            props.Add("color07", "\"Stack to RGB\",ImageK.Plugin.RGBStackConverter");
			props.Add("color08", "\"Make Composite\",ImageK.Plugin.CompositeConverter");
			props.Add("color09", "\"Show LUT\",ImageK.Plugin.filter.LutViewer");
			props.Add("color10", "\"Display LUTs\",ImageK.Plugin.SimpleCommands(\"display\")");
			props.Add("color11", "\"Edit LUT...\",ImageK.Plugin.LUT_Editor");
			props.Add("color12", "\"Color Picker...[K]\",ImageK.Plugin.frame.ColorPicker");

			// Plugins installed in the Image/Stacks submenu
			props.Add("stacks01", "\"Add Slice\",,ImageK.Plugin.StackEditor(\"add\")");
            props.Add("stacks02", "\"Delete Slice\",,ImageK.Plugin.StackEditor(\"delete\")");
            props.Add("stacks03", "\"Next Slice [>]\",,ImageK.Plugin.Animator(\"next\")");
            props.Add("stacks04", "\"Previous Slice [<]\",,ImageK.Plugin.Animator(\"previous\")");
            props.Add("stacks05", "\"Set Slice...\",,ImageK.Plugin.Animator(\"set\")");
            props.Add("stacks06", "-");
            props.Add("stacks07", "\"Images to Stack\",,ImageK.Plugin.ImagesToStack");
            props.Add("stacks08", "\"Stack to Images\",,ImageK.Plugin.StackEditor(\"toimages\")");
            props.Add("stacks09", "\"Make Montage...\",,ImageK.Plugin.MontageMaker");
            props.Add("stacks10", "\"Reslice [/]...\",,ImageK.Plugin.Slicer");
            props.Add("stacks11", "\"Orthogonal Views[H]\",,ImageK.Plugin.Orthogonal_Views");
            props.Add("stacks12", "\"Z Project...\",,ImageK.Plugin.ZProjector");
            props.Add("stacks13", "\"3D Project...\",,ImageK.Plugin.Projector");
            props.Add("stacks14", "\"Plot Z-axis Profile\",,ImageK.Plugin.ZAxisProfiler");
            props.Add("stacks15", "\"Measure Stack...\",,ImageK.Plugin.SimpleCommands(\"measure\")");
            props.Add("stacks16", "\"Label...\",,ImageK.Plugin.filter.StackLabeler");
            props.Add("stacks17", "\"Statistics\", ,ImageK.Plugin.Stack_Statistics");

			// Plugins installed in the Image/ Transform submenu
			props.Add("transform01", "\"Flip Horizontally\",ImageK.Plugin.Filter.Transformer(\"fliph\")");

            // Plugins installed in the Image/Lookup Tables submenu
            props.Add("lookup01", "\"Invert LUT\",ImageK.Plugin.LutLoader(\"invert\")");
			props.Add("lookup02", "\"Apply LUT\",ImageK.Plugin.filter.LutApplier");
			props.Add("lookup03", "-");
            props.Add("lookup04", "\"Fire\",ImageK.Plugin.LutLoader(\"fire\")");
			props.Add("lookup05", "\"Grays\",ImageK.Plugin.LutLoader(\"grays\")");
			props.Add("lookup06", "\"Ice\",ImageK.Plugin.LutLoader(\"ice\")");
			props.Add("lookup07", "\"Spectrum\",ImageK.Plugin.LutLoader(\"spectrum\")");
			props.Add("lookup08", "\"3-3-2 RGB\",ImageK.Plugin.LutLoader(\"3-3-2 RGB\")");
			props.Add("lookup09", "\"Red\",ImageK.Plugin.LutLoader(\"red\")");
			props.Add("lookup10", "\"Green\",ImageK.Plugin.LutLoader(\"green\")");
			props.Add("lookup11", "\"Blue\",ImageK.Plugin.LutLoader(\"blue\")");
			props.Add("lookup12", "\"Cyan\",ImageK.Plugin.LutLoader(\"cyan\")");
			props.Add("lookup13", "\"Magenta\",ImageK.Plugin.LutLoader(\"magenta\")");
			props.Add("lookup14", "\"Yellow\",ImageK.Plugin.LutLoader(\"yellow\")");
			props.Add("lookup15", "\"Red /Green\", ImageK.Plugin.LutLoader(\"redgreen\")");


			// Plugins installed in the Process/Binary submenu
            props.Add("binary01", "\"Make Binary\", ImageK.Plugin.Thresholder");
            props.Add("binary02", "\"Convert to Mask\",ImageK.Plugin.Thresholder(\"mask\")");
			props.Add("binary03", "-");
            props.Add("binary04", "\"Erode\",ImageK.Plugin.Filter.Binary(\"erode\")");
			props.Add("binary05", "\"Dilate\",ImageK.Plugin.Filter.Binary(\"dilate\")");
			props.Add("binary06", "\"Open\",ImageK.Plugin.Filter.Binary(\"open\")");
			// Can't use "Close" because it conflicts with File/Close
			props.Add("binary07", "\"Close -\", ImageK.Plugin.Filter.Binary(\"close\")");
			props.Add("binary08", "-");
            props.Add("binary09", "\"Outline\",ImageK.Plugin.Filter.Binary(\"outline\")");
			props.Add("binary10", "\"Fill Holes\",ImageK.Plugin.Filter.Binary(\"fill\")");
			props.Add("binary11", "\"Skeletonize\",ImageK.Plugin.Filter.Binary(\"skel\")");
			props.Add("binary12", "-");
            props.Add("binary13", "\"Distance Map\",ImageK.Plugin.Filter.EDM(\"edm\")");
			props.Add("binary14", "\"Ultimate Points\",ImageK.Plugin.Filter.EDM(\"points\")");
			props.Add("binary15", "\"Watershed\",ImageK.Plugin.Filter.EDM(\"watershed\")");
			props.Add("binary16", "\"Voronoi\",ImageK.Plugin.Filter.EDM(\"voronoi\")");
			props.Add("binary17", "-");
            props.Add("binary18", "\"Options...\",ImageK.Plugin.Filter.Binary(\"options\")");

            // Plugins installed in the Analyze/Tools submenu
            props.Add("tools01", "\"Save XY Coordinates...\", ImageK.Plugin.XYCoordinates");
            props.Add("tools02", "\"Fractal Box Count...\",ImageK.Plugin.Filter.FractalBoxCounter");
            props.Add("tools03", "\"Analyze Line Graph\",ImageK.Plugin.Filter.LineGraphAnalyzer");
            props.Add("tools04", "\"Curve Fitting...\",ImageK.Plugin.Frame.Fitter");
            props.Add("tools05", "\"ROI Manager...\",ImageK.Plugin.Frame.RoiManager");
            props.Add("tools06", "\"Scale Bar...\",ImageK.Plugin.ScaleBar");
            props.Add("tools07", "\"Calibration Bar...\",ImageK.Plugin.CalibrationBar");
            props.Add("tools08", "\"Synchronize Windows\",ImageK.Plugin.Frame.SyncWindows");
            props.Add("tools09", "\"Grid...\",ImageK.Plugin.Grid");

            // Plugins installed in the Plugins menu
            props.Add("plug-in01", ">\"Macros\"");
            props.Add("plug-in02", ">\"Shortcuts\"");
            props.Add("plug-in03", ">\"Utilities\"");
            props.Add("plug-in04", ">\"New_\"");
            props.Add("plug-in05", "\"Compile and Run...\",ImageK.Plugin.Compiler");
            props.Add("plug-in06", "\"Install... [M]\",ImageK.Plugin.PluginInstaller");

            // Plugins installed in the Plugins/Shortcuts submenu
            props.Add("shortcuts01", "\"Add Shortcut... \",ImageK.Plugin.Hotkeys(\"install\")");
            props.Add("shortcuts02", "\"Add Shortcut by Name... \", ImageK.Plugin.Hotkeys(\"install2\")");
            props.Add("shortcuts03", "\"Remove Shortcut...\",ImageK.Plugin.Hotkeys(\"remove\")");
            props.Add("shortcuts04", "\"List Shortcuts\",ImageK.Plugin.CommandLister(\"shortcuts\")");
            props.Add("shortcuts05", "\"List Commands\",ImageK.Plugin.Hotkeys(\"list\")");
            props.Add("shortcuts06", "-");

            //Plugins installed in the Plugins/Utilities submenu
            props.Add("utilities01", "\"Control Panel...[U]\", ImageK.Plugin.ControlPanel");
            props.Add("utilities02", "\"Find Commands...[l]\",ImageK.Plugin.CommandFinder");
            props.Add("utilities03", "\"Commands...[L]\",ImageK.Plugin.Frame.Commands");
            props.Add("utilities04", "\"Search...\",ImageK.Plugin.SimpleCommands(\"search\")");
            props.Add("utilities05", "\"Monitor Events...\",ImageK.Plugin.EventListener");
            props.Add("utilities06", "\"Monitor Memory...\",ImageK.Plugin.Frame.MemoryMonitor");
            props.Add("utilities07", "-");
            props.Add("utilities08", "\"Capture Screen[G]\",ImageK.Plugin.ScreenGrabber");
            props.Add("utilities09", "\"Capture Delayed...\",ImageK.Plugin.ScreenGrabber(\"delay\")");
            props.Add("utilities10", "\"Capture Image\",ImageK.Plugin.ScreenGrabber(\"image\")");
            props.Add("utilities11", "-");
            props.Add("utilities12", "\"ImageJ Properties\",ImageK.Plugin.JavaProperties");
            props.Add("utilities13", "\"Threads\",ImageK.Plugin.ThreadLister");
            props.Add("utilities14", "\"Benchmark\",ImageK.Plugin.Benchmark");
            props.Add("utilities15", "\"Reset...\",ImageK.Plugin.SimpleCommands(\"reset\")");

            loadPreferences();
            // loadOptions();
            // guiScale = get(GUI_SCALE, 1.0);
            foreach (var key in ijPrefs.Keys)
            {
                props.Add(key, ijPrefs[key]);
            }

            guiScale = FormUtil.GetScreenScalingFactor();
            // guiScale = get(GUI_SCALE, 1.0);
            return "";
        }


        public static string defaultResultsExtension()
        {
            return get("options.ext", ".csv");
        }

        /** Sets the GenericDialog and Command Finder text scale (0.5 to 3.0). */
        public static void setGuiScale(double scale)
        {
            if (scale>=0.5 && scale<=3.0)
            {
                guiScale = scale;
                set(GUI_SCALE, guiScale);
                Roi.resetDefaultHandleSize();
            }
        }

        /** Returns the GenericDialog and Command Finder text scale. */
        public static double getGuiScale()
        {
            return guiScale;
        }

        /** Returns the custom properties (IJ_Props.txt) file path. */
        public static string getCustomPropsPath()
        {
            return propertiesPath;
        }

        /** Returns the custom preferences (IJ_Prefs.txt) file path. */
        public static string getCustomPrefsPath()
        {
            return preferencesPath;
        }

        /** Retrieves a string from string in IJ_Props or IJ_Prefs.txt. */
        public static string getString(string key)
        {
            if (props.ContainsKey(key))
            {
                return props[key];
			}
            else
            {
                return null;
            }
        }


		/** Retrieves a number from IJ_Props or IJ_Prefs.txt.
	    Does not retrieve numbers set using Prefs.set(). */
		public static int getInt(string key, int defaultValue)
        {
            if (props == null) //workaround for Netscape JIT bug
                return defaultValue;
            string? s = props.ContainsKey(key) ? props[key] : null;
            if (s != null)
            {
                try
                {
                    return int.Parse(s);
                }
                catch (FormatException e) { IJ.log("" + e); }
            }
            return defaultValue;
        }

        /** Retrieves a number from IJ_Props or IJ_Prefs.txt.
        Does not retrieve numbers set using Prefs.set(). */
        public static double getDouble(string key, double defaultValue)
        {
            if (props == null)
                return defaultValue;
            double d = double.NaN;
            if (props.ContainsKey(key))
            {
                string s = props[key];
                try { d = Convert.ToDouble(s); }
                catch (FormatException e) { d = double.NaN; }
                if (!double.IsNaN(d))
                    return d;
            }
            return defaultValue;
        }

		/** Returns the number of threads used by PlugInFilters to process images and stacks. */
		public static int getThreads()
        {
            if (threads == 0)
            {
                threads = getInt(THREADS, 0);
                // int processors = Runtime.getRuntime().availableProcessors();
				int processors = Environment.ProcessorCount;
				if (threads < 1 || threads > processors)
                    threads = processors;
            }
            return threads;
        }

        /** Saves the value of the string <code>text</code> in the preferences
         * file using the keyword <code>key</code>. The string can be 
         * retrieved using the appropriate <code>get()</code> method.
         * @see #get(String,String)
        */
        public static void set(string key, string text)
        {
            if (key.IndexOf('.') < 1)
                throw new ArgumentException("Key must have a prefix");
            if (text == null)
                ijPrefs.Remove(KEY_PREFIX + key);
            else
                ijPrefs[KEY_PREFIX + key] = text;
        }

        /** Saves the value of the integer <code>value</code> in the preferences
         * file using the keyword <code>key</code>. The value can be 
         * retrieved using the appropriate <code>get()</code> method.
         * @see #get(String,double)
        */
        public static void set(string key, int value)
        {
            set(key, value.ToString());
        }

        /** Saves the value of the double <code>value</code> in the preferences
         * file using the keyword <code>key</code>. The value can be
         * retrieved using the appropriate <code>get()</code> method.
         * @see #get(String,double)
        */
        public static void set(string key, double value)
        {
            set(key, ""+value);
        }

        /** Saves the value of the boolean <code>value</code> in the preferences
         * file using the keyword <code>key</code>. The value can be
         * retrieved using the appropriate <code>get()</code> method.
         * @see #get(String,boolean)
        */
        public static void set(string key, bool value)
        {
            set(key, ""+value);
        }

        /** Uses the keyword <code>key</code> to retrieve a string from the
        preferences file. Returns <code>defaultValue</code> if the key
        is not found. */
        public static string get(string key, string defaultValue)
        {
            string value = ijPrefs[KEY_PREFIX+key];
            if (value == null)
                return defaultValue;
            else
                return value;
        }

        /** Uses the keyword <code>key</code> to retrieve a number from the
        preferences file. Returns <code>defaultValue</code> if the key
        is not found. */
        public static double get(string key, double defaultValue)
        {
            string s = ijPrefs.getProperty(KEY_PREFIX+key);
            if (!string.IsNullOrEmpty(s))
            {
                try
                {
                    var d = double.Parse(s);
                    return d;
                }
                catch (FormatException e)
                {
                    return defaultValue;
                }
            }
            return defaultValue;
        }

        /** Uses the keyword <code>key</code> to retrieve a boolean from
        the preferences file. Returns <code>defaultValue</code> if
        the key is not found. */
        public static bool get(string key, bool defaultValue)
        {
            string value = ijPrefs.getProperty(KEY_PREFIX+key);
            if (string.IsNullOrEmpty(value))
                return defaultValue;
            else
                return value.Equals("true");
        }

        /** Obsolete, replaced by getImageJDir(), which, unlike this method, 
        returns a path that ends with File.separator. */
        public static string getHomeDir()
        {
            return ImageJDir;
        }

        /** Returns the path, ending in File.separator, to the ImageJ directory. */
        public static string getImageJDir()
        {
            string path = Menus.getImageJPath();
            if (path==null)
            {
                string ijPath = getPluginsDirProperty();
                //if (ijPath==null)
                //	ijPath = ImageJDir;
                if (ijPath == null)
                {
                    // ijPath = System.getProperty("user.dir");
                    ijPath = Directory.GetCurrentDirectory();
                }

                return ijPath + Path.DirectorySeparatorChar;
            }
            else
                return path;
        }

        public static string getPluginsDirProperty()
        {
            if (pluginsDirProperty==null)
            {
                // String ijDir = System.getProperty("plugins.dir");
                string ijDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins");
                if (ijDir!=null)
                {
                    if (ijDir.EndsWith("/")||ijDir.EndsWith("\\"))
                        ijDir = ijDir.Substring(0, ijDir.Length-1);
                    if (ijDir.EndsWith("/plugins")||ijDir.EndsWith("\\plugins"))
                        ijDir = ijDir.Substring(0, ijDir.Length-8);
                    pluginsDirProperty = ijDir;
                }
                else
                    pluginsDirProperty = "";
            }
            return pluginsDirProperty.Length>0 ? pluginsDirProperty : null;
        }

        /** Returns the path to the directory where the 
        preferences file (IJPrefs.txt) is saved. */
        public static string getPrefsDir()
        {
            // look in current directory
            if (prefsDir==null)
            {
                string cwd = Directory.GetCurrentDirectory();
                if (File.Exists(Path.Combine(cwd, PREFS_NAME)))
                {
                    prefsDir = cwd;
                    preferencesPath = cwd+"/"+PREFS_NAME;
                }
                // look in ImageJ directory
                if (prefsDir==null)
                {
                    String ijDir = getImageJDir();
                    ijDir = ijDir.Substring(0, ijDir.Length-1);
                    if (File.Exists(Path.Combine(ijDir, PREFS_NAME)))
                    {
                        prefsDir = ijDir;
                        preferencesPath = ijDir+"/"+PREFS_NAME;
                    }
                }
                // use home directory
                if (prefsDir==null)
                {
                    // String dir = System.getProperty("user.home");
                    string dir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
                    if (IJ.isMacOSX())
                        dir += "/Library/Preferences";
                    else
                        dir += Path.DirectorySeparatorChar + ".imagek";
                    prefsDir = dir;
                }
            }
            return prefsDir;
        }

        /** Sets the path to the ImageJ directory. */
        static void setHomeDir(string path)
        {
            if (path.EndsWith(Path.DirectorySeparatorChar))
                path = path.Substring(0, path.Length-1);
            ImageJDir = path;
        }

        /** Returns the default directory, if any, or null. */
        public static String getDefaultDirectory()
        {
            if (commandLineMacro)
                return null;
            else
                return getString(DIR_IMAGE);
        }

        /** Returns the file.separator system property. */
        public static String getFileSeparator()
        {
            return separator;
        }

        /** Opens the ImageJ preferences file ("IJ_Prefs.txt") file. */
        static void loadPreferences()
        {
            string path = getPrefsDir()+separator+PREFS_NAME;
            bool ok = loadPrefs(path);
            if (!ok)
            { // not found
                if (IJ.IsWindows())
                    path = ImageJDir +separator+PREFS_NAME;
                else
                    path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + separator + PREFS_NAME; //User's home dir
                ok = loadPrefs(path);
                if (ok)
                    File.Delete(path);
            }

        }

        static bool loadPrefs(string path)
        {
            try
            {
                using StreamReader streamReader =
                    new StreamReader(System.IO.File.OpenRead(path));
                var str = streamReader.ReadToEnd();
                ijPrefs = JsonSerializer.Deserialize<Dictionary<string,string>>(str);
                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }

        /** Saves user preferences in the IJ_Prefs.txt properties file. */
        public static void savePreferences()
        {
            string path = null;
            commandLineMacro = false;
            try
            {
                Dictionary<string,string> prefs = new Dictionary<string, string>();
                string dir = OpenDialog.getDefaultDirectory();
                if (dir!=null)
                    prefs[DIR_IMAGE] = dir;
                prefs[ROICOLOR] = Tools.c2hex(Roi.getColor());
                //todo:
                // prefs[SHOW_ALL_COLOR] = Tools.c2hex(ImageCanvas.getShowAllColor()));
                // prefs[FCOLOR] = Tools.c2hex(Toolbar.getForegroundColor()));
                // prefs[BCOLOR] = Tools.c2hex(Toolbar.getBackgroundColor()));
                // prefs[JPEG] = Integer.toString(FileSaver.getJpegQuality()));
                // prefs[FPS] = Double.toString(Animator.getFrameRate()));
                // prefs[DIV_BY_ZERO_VALUE] = Double.toString(FloatBlitter.divideByZeroValue));
                // prefs[NOISE_SD] = Double.toString(Filters.getSD()));
                // if (threads>1) prefs.put(THREADS, Integer.toString(threads));
                if (IJ.isMacOSX()) useJFileChooser = false;
                if (!IJ.isLinux()) dialogCancelButtonOnRight = false;
                saveOptions(prefs);
                savePluginPrefs(prefs);
                ImageK ij = IJ.GetInstance();
                if (ij!=null)
                    ij.savePreferences(prefs);
                Menus.savePreferences(prefs);
                // ParticleAnalyzer.savePreferences(prefs);
                // Analyzer.savePreferences(prefs);
                // ImportDialog.savePreferences(prefs);
                // PlotWindow.savePreferences(prefs);
                // NewImage.savePreferences(prefs);
                string prefsDir = getPrefsDir();
                path = prefsDir+separator+PREFS_NAME;
                if (prefsDir.EndsWith(".imagek"))
                {
                    if (!Directory.Exists(prefsDir)) Directory.CreateDirectory(prefsDir); // create .imagej directory
                }
                if (_resetPreferences)
                {
                    if (!File.Exists(path))
                        IJ.error("Edit>Options>Reset", "Unable to reset preferences. File not found at\n"+path);
                    File.Delete(path);
                    _resetPreferences = false;
                }
                else
                    savePrefs(prefs, path);
            }
            catch (Exception t)
            {
                string msg = t.Message;
                if (msg==null) msg = ""+t;
                int delay = 4000;
                try
                {
                    new TextWindow("Error Saving Preferences:\n"+path, msg, 500, 200);
                    IJ.wait(delay);
                }
                catch (Exception t2) { }
            }
        }

        /** Delete the preferences file when ImageJ quits. */
        public static void resetPreferences()
        {
            _resetPreferences = true;
        }

        static void loadOptions()
        {
            int defaultOptions = ANTIALIASING+AVOID_RESLICE_INTERPOLATION+ANTIALIASED_TOOLS+MULTI_POINT_MODE
                +(!IJ.isMacOSX() ? RUN_SOCKET_LISTENER : 0)+BLACK_BACKGROUND;
            int options = getInt(OPTIONS, defaultOptions);
            usePointerCursor = (options&USE_POINTER)!=0;
            //antialiasedText = (options&ANTIALIASING)!=0;
            antialiasedText = false;
            interpolateScaledImages = (options&INTERPOLATE)!=0;
            open100Percent = (options&ONE_HUNDRED_PERCENT)!=0;
            blackBackground = (options&BLACK_BACKGROUND)!=0;
            useJFileChooser = (options&JFILE_CHOOSER)!=0;
            weightedColor = (options&WEIGHTED)!=0;
            if (weightedColor)
                ColorProcessor.setWeightingFactors(0.299, 0.587, 0.114);
            blackCanvas = (options&BLACK_CANVAS)!=0;
            requireControlKey = (options&REQUIRE_CONTROL)!=0;
            useInvertingLut = (options&USE_INVERTING_LUT)!=0;
            intelByteOrder = (options&INTEL_BYTE_ORDER)!=0;
            noBorder = (options&NO_BORDER)!=0;
            showAllSliceOnly = (options&SHOW_ALL_SLICE_ONLY)!=0;
            copyColumnHeaders = (options&COPY_HEADERS)!=0;
            noRowNumbers = (options&NO_ROW_NUMBERS)!=0;
            moveToMisc = (options&MOVE_TO_MISC)!=0;
            runSocketListener = (options&RUN_SOCKET_LISTENER)!=0;
            multiPointMode = (options&MULTI_POINT_MODE)!=0;
            rotateYZ = (options&ROTATE_YZ)!=0;
            flipXZ = (options&FLIP_XZ)!=0;
            //dontSaveHeaders = (options&DONT_SAVE_HEADERS)!=0;
            //dontSaveRowNumbers = (options&DONT_SAVE_ROW_NUMBERS)!=0;
            noClickToGC = (options&NO_CLICK_TO_GC)!=0;
            avoidResliceInterpolation = (options&AVOID_RESLICE_INTERPOLATION)!=0;
            keepUndoBuffers = (options&KEEP_UNDO_BUFFERS)!=0;

            defaultOptions = (!IJ.isMacOSX() ? USE_FILE_CHOOSER : 0);
            int options2 = getInt(OPTIONS2, defaultOptions);
            useSystemProxies = (options2&USE_SYSTEM_PROXIES)!=0;
            useFileChooser = (options2&USE_FILE_CHOOSER)!=0;
            subPixelResolution = (options2&SUBPIXEL_RESOLUTION)!=0;
            enhancedLineTool = (options2&ENHANCED_LINE_TOOL)!=0;
            skipRawDialog = (options2&SKIP_RAW_DIALOG)!=0;
            reverseNextPreviousOrder = (options2&REVERSE_NEXT_PREVIOUS_ORDER)!=0;
            autoRunExamples = (options2&AUTO_RUN_EXAMPLES)!=0;
            showAllPoints = (options2&SHOW_ALL_POINTS)!=0;
            doNotSaveWindowLocations = (options2&DO_NOT_SAVE_WINDOW_LOCS)!=0;
            jFileChooserSettingChanged = (options2&JFILE_CHOOSER_CHANGED)!=0;
            dialogCancelButtonOnRight = (options2&CANCEL_BUTTON_ON_RIGHT)!=0;
            ignoreRescaleSlope = (options2&IGNORE_RESCALE_SLOPE)!=0;
            nonBlockingFilterDialogs = (options2&NON_BLOCKING_DIALOGS)!=0;
            fixedDicomScaling = (options2&FIXED_DICOM_SCALINGg)!=0;
        }

        static void saveOptions(Dictionary<string,string> prefs)
        {
            int options = (usePointerCursor ? USE_POINTER : 0) + (antialiasedText ? ANTIALIASING : 0)
                + (interpolateScaledImages ? INTERPOLATE : 0) + (open100Percent ? ONE_HUNDRED_PERCENT : 0)
                + (blackBackground ? BLACK_BACKGROUND : 0) + (useJFileChooser ? JFILE_CHOOSER : 0)
                + (blackCanvas ? BLACK_CANVAS : 0) + (weightedColor ? WEIGHTED : 0)
                + (requireControlKey ? REQUIRE_CONTROL : 0)
                + (useInvertingLut ? USE_INVERTING_LUT : 0)
                + (intelByteOrder ? INTEL_BYTE_ORDER : 0) + (doubleBuffer ? DOUBLE_BUFFER : 0)
                + (noPointLabels ? NO_POINT_LABELS : 0) + (noBorder ? NO_BORDER : 0)
                + (showAllSliceOnly ? SHOW_ALL_SLICE_ONLY : 0) + (copyColumnHeaders ? COPY_HEADERS : 0)
                + (noRowNumbers ? NO_ROW_NUMBERS : 0) + (moveToMisc ? MOVE_TO_MISC : 0)
                + (runSocketListener ? RUN_SOCKET_LISTENER : 0)
                + (multiPointMode ? MULTI_POINT_MODE : 0) + (rotateYZ ? ROTATE_YZ : 0)
                + (flipXZ ? FLIP_XZ : 0) + (dontSaveHeaders ? DONT_SAVE_HEADERS : 0)
                + (dontSaveRowNumbers ? DONT_SAVE_ROW_NUMBERS : 0) + (noClickToGC ? NO_CLICK_TO_GC : 0)
                + (avoidResliceInterpolation ? AVOID_RESLICE_INTERPOLATION : 0)
                + (keepUndoBuffers ? KEEP_UNDO_BUFFERS : 0);
            prefs[OPTIONS] = options.ToString();

            int options2 = (useSystemProxies ? USE_SYSTEM_PROXIES : 0)
                + (useFileChooser ? USE_FILE_CHOOSER : 0) + (subPixelResolution ? SUBPIXEL_RESOLUTION : 0)
                + (enhancedLineTool ? ENHANCED_LINE_TOOL : 0) + (skipRawDialog ? SKIP_RAW_DIALOG : 0)
                + (reverseNextPreviousOrder ? REVERSE_NEXT_PREVIOUS_ORDER : 0)
                + (autoRunExamples ? AUTO_RUN_EXAMPLES : 0) + (showAllPoints ? SHOW_ALL_POINTS : 0)
                + (doNotSaveWindowLocations ? DO_NOT_SAVE_WINDOW_LOCS : 0)
                + (jFileChooserSettingChanged ? JFILE_CHOOSER_CHANGED : 0)
                + (dialogCancelButtonOnRight ? CANCEL_BUTTON_ON_RIGHT : 0)
                + (ignoreRescaleSlope ? IGNORE_RESCALE_SLOPE : 0)
                + (nonBlockingFilterDialogs ? NON_BLOCKING_DIALOGS : 0)
                + (fixedDicomScaling ? FIXED_DICOM_SCALINGg : 0);
            prefs[OPTIONS2] = options2.ToString();
        }

        /** Saves the Point <code>loc</code> in the preferences
        file as a string using the keyword <code>key</code>. */
        public static void saveLocation(string key, Point loc)
        {
            if (!doNotSaveWindowLocations)
                set(key, loc!=null ? loc.X+","+loc.Y : null);
        }

        /** Save plugin preferences. */
        static void savePluginPrefs(Dictionary<string,string> prefs)
        {
            var e = ijPrefs.Keys;
            foreach (var key in e)
            {
                if (key.IndexOf(KEY_PREFIX) == 0)
                    prefs[key] = ijPrefs[key];
            }
        }

        public static void savePrefs(Dictionary<string, string> prefs, string path)
        {
           try
           {
               var json = JsonSerializer.Serialize(prefs, new JsonSerializerOptions()
               {
                   WriteIndented = true,
                   PropertyNameCaseInsensitive = true //不敏感大小写
               });

                File.WriteAllText(path, json);
           }
           catch (Exception ex)
           {
               
           }
        }


        /** Finds a color in IJ_Props or IJ_Prefs.txt. */
        public static Color getColor(String key, Color defaultColor)
        {
            int i = getInt(key, 0xaaa);
            if (i == 0xaaa)
                return defaultColor;
            return Color.FromArgb((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
        }

        // public static bool commandLineMacro()
        // {
        //     return commandLineMacro;
        // }
    }
}
